/**
 * @Project TMHSC
 * @Author Tran Manh Hung
 * @Email: hungtmit@gmail.com | tmh@xwebgate.com
 * @Copyright © 2011 TMH. All rights reserved
 * @Createdate Jan 31, 2011
 * @Createtime 12:42:20 AM
 */

package com.tmh.study.aASA
{
	import flash.display.Sprite;
	import flash.utils.getTimer;

	/**
	 * 	collision check base on grid
	 */
	public class GridCollision extends Sprite
	{
		private const GRID_SIZE:Number = 50; // grid size must be at least as large as largest object!!!

		private const RADIUS:Number = 25;

		private var _balls:Array;

		private var _grid:Array;

		private var _numBalls:int = 1000;

		private var _numChecks:int = 0;

		public function GridCollision()
		{
			makeBall();
			drawGrid();

			var startTime:int;
			var elapsed:int;
			var i:int;

			// get initial time
			startTime = getTimer();

			for (i = 0; i < 10; i++)
			{
				makeGrid();
				assignBallsToGrid();
				checkGrid();
			}

			elapsed = getTimer() - startTime;
			trace("Grid check: ", elapsed);

		/*
		startTime = getTimer();

		for (i = 0; i < 10; i++)
		{
			basicCheck();
		}

		elapsed = getTimer() - startTime;
		trace("Basic check: ", elapsed);

		trace(_numChecks);
		*/
		}

		private function makeBall():void
		{
			_balls = new Array();

			for (var i:uint = 0; i < _numBalls; i++)
			{
				// create ball and add it to the display list and the _balls Array
				var ball:Ball = new Ball(RADIUS);
				ball.x = Math.random() * stage.stageWidth;
				ball.y = Math.random() * stage.stageHeight;
				addChild(ball);
				_balls.push(ball);
			}
		}

		private function makeGrid():void
		{
			_grid = new Array();

			// stage.stageWidth / GRID_SIZE = number of columns
			for (var i:uint = 0; i < stage.stageWidth / GRID_SIZE; i++)
			{
				_grid[i] = new Array();

				// stage.stageHeight / GRID_SIZE = number of rows
				for (var j:uint = 0; j < stage.stageHeight / GRID_SIZE; j++)
				{
					_grid[i][j] = new Array();
				}
			}
		}

		private function drawGrid():void
		{
			graphics.lineStyle(0, .5);

			for (var i:int = 0; i < stage.stageWidth; i += GRID_SIZE)
			{
				graphics.moveTo(i, 0);
				graphics.lineTo(i, stage.stageHeight);
			}

			for (i = 0; i < stage.stageHeight; i += GRID_SIZE)
			{
				graphics.moveTo(0, i);
				graphics.lineTo(stage.stageWidth, i);
			}
		}

		private function assignBallsToGrid():void
		{
			for (var i:int = 0; i < _numBalls; i++)
			{
				// dividing position by grid size, tell us which row and column each ball is in
				var ball:Ball = _balls[i] as Ball;
				var xpos:int = Math.floor(ball.x / GRID_SIZE);
				var ypos:int = Math.floor(ball.y / GRID_SIZE);
				(_grid[xpos][ypos] as Array).push(ball);
			}
		}

		private function basicCheck():void
		{
			for (var i:int; i < _balls.length - 1; i++)
			{
				var ballA:Ball = _balls[i] as Ball;

				for (var j:int = i + 1; j < _balls.length; j++)
				{
					var ballB:Ball = _balls[j] as Ball;
					checkCollision(ballA, ballB);
				}
			}
		}

		private function checkGrid():void
		{
			// loop through each row and column of grid
			for (var i:int = 0; i < _grid.length; i++)
			{
				for (var j:int = 0; j < _grid[i].length; j++)
				{
					// examine all object in the first cell against each other
					checkOneCell(i, j);
					checkTwoCell(i, j, i + 1, j); // cell to the right
					checkTwoCell(i, j, i - 1, j + 1); // below to the left
					checkTwoCell(i, j, i, j + 1); // direct below
					checkTwoCell(i, j, i + 1, j + 1); // below to the right
				}
			}
		}

		private function checkOneCell(x:int, y:int):void
		{
			// check all the balls in a single cell against each other
			var cell:Array = _grid[x][y] as Array;

			for (var i:int = 0; i < cell.length - 1; i++)
			{
				var ballA:Ball = cell[i] as Ball;

				for (var j:int = i + 1; j < cell.length; j++)
				{
					var ballB:Ball = cell[j] as Ball;
					checkCollision(ballA, ballB);
				}
			}
		}

		private function checkTwoCell(x1:int, y1:int, x2:int, y2:int):void
		{
			// make sure the second cell really exists
			if (x2 < 0)
			{
				return;
			}

			if (x2 >= _grid.length)
			{
				return;
			}

			if (y2 >= _grid[x2].length)
			{
				return;
			}

			var cell0:Array = _grid[x1][y1] as Array;
			var cell1:Array = _grid[x2][y2] as Array;

			// check all the ball in one cell against all in other
			for (var i:int = 0; i < cell0.length; i++)
			{
				var ballA:Ball = cell0[i] as Ball;

				for (var j:int = 0; j < cell1.length; j++)
				{
					var ballB:Ball = cell1[j] as Ball;

					checkCollision(ballA, ballB);
				}
			}
		}

		private function checkCollision(ballA:Ball, ballB:Ball):void
		{
			//_numChecks++;
			var dx:Number = ballB.x - ballA.x;
			var dy:Number = ballB.y - ballA.y;
			var dist:Number = Math.sqrt(dx * dx + dy * dy);

			// if distance is less than sum of radii, collision
			if (dist < (ballA.radius + ballB.radius))
			{
				ballA.color = 0xcc0000;
				ballB.color = 0x00cc00;
			}
		}
	}
}